{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Semantic Kernel \n",
    "\n",
    "In this code sample, you will use the [Semantic Kernel](https://aka.ms/ai-agents-beginners/semantic-kernel) AI Framework to create a basic agent. \n",
    "\n",
    "The goal of this sample is to show you the steps that we will later use in the addtional code samples when implementing the different agentic patterns. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import the Needed Python Packages "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "\n",
    "from typing import Annotated\n",
    "from openai import AsyncOpenAI\n",
    "\n",
    "\n",
    "from semantic_kernel.kernel import Kernel\n",
    "from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
    "from semantic_kernel.agents import ChatCompletionAgent\n",
    "from semantic_kernel.contents import ChatHistory\n",
    "\n",
    "\n",
    "from semantic_kernel.agents.open_ai import OpenAIAssistantAgent\n",
    "from semantic_kernel.contents import AuthorRole, ChatMessageContent\n",
    "from semantic_kernel.functions import kernel_function\n",
    "\n",
    "from semantic_kernel.connectors.ai import FunctionChoiceBehavior\n",
    "\n",
    "from semantic_kernel.contents.function_call_content import FunctionCallContent\n",
    "from semantic_kernel.contents.function_result_content import FunctionResultContent\n",
    "from semantic_kernel.functions import KernelArguments, kernel_function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the Client and Kernel \n",
    "\n",
    "In this sample, we will use [GitHub Models](https://aka.ms/ai-agents-beginners/github-models) for access to the LLM. \n",
    "\n",
    "The `ai_model_id` is defined as `gpt-4o-mini`. Try changing the model to another model available on the GitHub Models marketplace to see the different results. \n",
    "\n",
    "For us to us the `Azure Inference SDK` that is used for the `base_url` for GitHub Models, we will use the `AsyncOpenAI` connector within Semantic Kernel. There are also other [available connectors](https://learn.microsoft.com/semantic-kernel/concepts/ai-services/chat-completion) to use Semantic Kernel for other model providers.\n",
    "\n",
    "We will also create a `Kernel`. A `kernel` is a collection of the services and plugins that will be used by your Agents. In this snipppet, we are creating the kernel and adding the `chat_completion_service` to it.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random   \n",
    "\n",
    "# Define a sample plugin for the sample\n",
    "\n",
    "class DestinationsPlugin:\n",
    "    \"\"\"A List of Random Destinations for a vacation.\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        # List of vacation destinations\n",
    "        self.destinations = [\n",
    "            \"Barcelona, Spain\",\n",
    "            \"Paris, France\",\n",
    "            \"Berlin, Germany\",\n",
    "            \"Tokyo, Japan\",\n",
    "            \"Sydney, Australia\",\n",
    "            \"New York, USA\",\n",
    "            \"Cairo, Egypt\",\n",
    "            \"Cape Town, South Africa\",\n",
    "            \"Rio de Janeiro, Brazil\",\n",
    "            \"Bali, Indonesia\"\n",
    "        ]\n",
    "        # Track last destination to avoid repeats\n",
    "        self.last_destination = None\n",
    "\n",
    "    @kernel_function(description=\"Provides a random vacation destination.\")\n",
    "    def get_random_destination(self) -> Annotated[str, \"Returns a random vacation destination.\"]:\n",
    "        # Get available destinations (excluding last one if possible)\n",
    "        available_destinations = self.destinations.copy()\n",
    "        if self.last_destination and len(available_destinations) > 1:\n",
    "            available_destinations.remove(self.last_destination)\n",
    "\n",
    "        # Select a random destination\n",
    "        destination = random.choice(available_destinations)\n",
    "\n",
    "        # Update the last destination\n",
    "        self.last_destination = destination\n",
    "\n",
    "        return destination"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "load_dotenv()\n",
    "client = AsyncOpenAI(\n",
    "    api_key=os.environ.get(\"GITHUB_TOKEN\"), base_url=\"https://models.inference.ai.azure.com/\")\n",
    "\n",
    "kernel = Kernel()\n",
    "kernel.add_plugin(DestinationsPlugin(), plugin_name=\"destinations\")\n",
    "\n",
    "service_id = \"agent\"\n",
    "\n",
    "chat_completion_service = OpenAIChatCompletion(\n",
    "    ai_model_id=\"gpt-4o-mini\",\n",
    "    async_client=client,\n",
    "    service_id=service_id\n",
    ")\n",
    "kernel.add_service(chat_completion_service)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the Agent \n",
    "\n",
    "Below we will are creating the Agent called `TravelAgent` and also creating a variable called `AGENT_INSTRUCTIONS`. We will later add this to our `system_message` that will give the agent instructions on the task, behavior and tone.\n",
    "\n",
    "For this example, we are using very simple instructions. You can change these instructions to see how the agent responds differently. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "settings = kernel.get_prompt_execution_settings_from_service_id(\n",
    "    service_id=service_id)\n",
    "settings.function_choice_behavior = FunctionChoiceBehavior.Auto()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "AGENT_NAME = \"TravelAgent\"\n",
    "AGENT_INSTRUCTIONS = \"You are a helpful AI Agent that can help plan vacations for customers at random destinations\"\n",
    "agent = ChatCompletionAgent(\n",
    "    kernel=kernel, \n",
    "    name=AGENT_NAME,\n",
    "    instructions=AGENT_INSTRUCTIONS,\n",
    "    arguments=KernelArguments(settings=settings)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running the Agents \n",
    "\n",
    "Now we can run the Agent by defining the `ChatHistory` and adding the `system_message` to it. We will use the `AGENT_INSTRUCTIONS` that we defined earlier. \n",
    "\n",
    "After these are defined, we create a `user_inputs` that will be what the user is sending to the agent. In this case, we have set this message to `Plan me a sunny vacation`. \n",
    "\n",
    "Feel free to change this message to see how the agent responds differently. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import display, HTML\n",
    "\n",
    "\n",
    "async def main():\n",
    "    # Define the chat history\n",
    "    chat_history = ChatHistory()\n",
    "\n",
    "    # Respond to user input\n",
    "    user_inputs = [\n",
    "        \"Plan me a day trip.\",\n",
    "        \"I don't like that destination. Plan me another vacation.\",\n",
    "    ]\n",
    "\n",
    "    for user_input in user_inputs:\n",
    "        # Add the user input to the chat history\n",
    "        chat_history.add_user_message(user_input)\n",
    "\n",
    "        # Start building HTML output\n",
    "        html_output = f\"<div style='margin-bottom:10px'>\"\n",
    "        html_output += f\"<div style='font-weight:bold'>User:</div>\"\n",
    "        html_output += f\"<div style='margin-left:20px'>{user_input}</div>\"\n",
    "        html_output += f\"</div>\"\n",
    "\n",
    "        agent_name: str | None = None\n",
    "        full_response = \"\"\n",
    "        function_calls = []\n",
    "        function_results = {}\n",
    "\n",
    "        # Collect the agent's response with function call tracking\n",
    "        async for content in agent.invoke_stream(chat_history):\n",
    "            if not agent_name and hasattr(content, 'name'):\n",
    "                agent_name = content.name\n",
    "\n",
    "            # Track function calls and results\n",
    "            for item in content.items:\n",
    "                if isinstance(item, FunctionCallContent):\n",
    "                    call_info = f\"Calling: {item.function_name}({item.arguments})\"\n",
    "                    function_calls.append(call_info)\n",
    "                elif isinstance(item, FunctionResultContent):\n",
    "                    result_info = f\"Result: {item.result}\"\n",
    "                    function_calls.append(result_info)\n",
    "                    # Store function results\n",
    "                    function_results[item.function_name] = item.result\n",
    "\n",
    "            # Add content to response if it's not a function-related message\n",
    "            if (hasattr(content, 'content') and content.content and content.content.strip() and\n",
    "                not any(isinstance(item, (FunctionCallContent, FunctionResultContent))\n",
    "                        for item in content.items)):\n",
    "                full_response += content.content\n",
    "\n",
    "        # Add function calls to HTML if any occurred\n",
    "        if function_calls:\n",
    "            html_output += f\"<div style='margin-bottom:10px'>\"\n",
    "            html_output += f\"<details>\"\n",
    "            html_output += f\"<summary style='cursor:pointer; font-weight:bold; color:#0066cc;'>Function Calls (click to expand)</summary>\"\n",
    "            html_output += f\"<div style='margin:10px; padding:10px; background-color:#f8f8f8; border:1px solid #ddd; border-radius:4px; white-space:pre-wrap;'>\"\n",
    "            html_output += \"<br>\".join(function_calls)\n",
    "            html_output += f\"</div></details></div>\"\n",
    "\n",
    "        # Add agent response to HTML\n",
    "        html_output += f\"<div style='margin-bottom:20px'>\"\n",
    "        html_output += f\"<div style='font-weight:bold'>{agent_name or 'Assistant'}:</div>\"\n",
    "        html_output += f\"<div style='margin-left:20px; white-space:pre-wrap'>{full_response}</div>\"\n",
    "        html_output += f\"</div>\"\n",
    "        html_output += \"<hr>\"\n",
    "\n",
    "        # Display formatted HTML\n",
    "        display(HTML(html_output))\n",
    "\n",
    "await main()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
